/*
	glOutput Team
*/

//#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

struct Player {
	// position of cube (x,y,z) is center or cube
	float pos[3];
	float length;
	int direction;
	float speed;
};

struct Room { 
	// position of room (x,y,z) is center or room
	float pos[3];
	// length of one side of room
	float length;
};

/*struct Situation {
	struct Player player1;
	struct Player player2;
	struct Room room;
};*/

// GLOBAL VARIABLES //

//global variables to keep track of window width and height.
//set to initial values for convenience, but we need variables
//for later on in case the window gets resized.
int windowWidth = 512, windowHeight = 512;

double theta = 0.0;
float cameraTheta, cameraPhi, cameraRadius; //camera position in spherical coordinates
float cameraX, cameraY, cameraZ;            //camera position in cartesian coordinates
struct Player player1;
struct Player player2;
struct Room room;

// END GLOBAL VARIABLES //

//
// void computeArcballPosition(float theta, float phi, float radius,
//                              float &x, float &y, float &z);
//
// This function updates the camera's position in cartesian coordinates based 
//  on its position in spherical coordinates. The user passes in the current phi,
//  theta, and radius, as well as variables to hold the resulting X, Y, and Z values.
//  Those variables for X, Y, and Z values get filled with the camera's position in R3.
//
void computeArcballPosition(float theta, float phi, float radius,
                            float &x, float &y, float &z) {
    x = radius * sinf(theta)*sinf(phi);
    y = radius * -cosf(phi);
    z = radius * -cosf(theta)*sinf(phi);
}

void drawPlayer(Player player)
{
	// draws cube
	glPushMatrix();
	    glTranslatef(player.pos[0], player.pos[1], player.pos[2]);
		glutSolidCube( player.length );
	glPopMatrix();
}

void drawRoom(Room room)
{
	// draws cube
	glPushMatrix();
	    glTranslatef(room.pos[0], room.pos[1], room.pos[2]);
		glutWireCube( room.length );
	glPopMatrix();
}
//
//  void resizeWindow(int w, int h)
//
//      We will register this function as GLUT's reshape callback.
//   When the window is resized, the OS will tell GLUT and GLUT will tell
//   us by calling this function - GLUT will give us the new window width
//   and height as 'w' and 'h,' respectively - so save those to our global vars.
//
void resizeWindow(int w, int h) {
    float aspectRatio = w / (float)h;

    windowWidth = w;
    windowHeight = h;

    //update the viewport to fill the window
    glViewport(0, 0, w, h);

    //update the projection matrix with the new window properties
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0,aspectRatio,0.1,100000);
}

// used for testing
// returns 1 if passed cube position is in room, returns 0 otherwise
int checkInput(Player player, Room room)
{
	if(player.pos[0] < (room.pos[0] - room.length/2)  || player.pos[0] > (room.pos[0] + room.length/2))
	  return 0;
	
	if(player.pos[1] < (room.pos[1] - room.length/2)  || player.pos[1] > (room.pos[1] + room.length/2))
	  return 0;
	
	if(player.pos[2] < (room.pos[2] - room.length/2)  || player.pos[2] > (room.pos[2] + room.length/2))
	  return 0;
	  
	return 1;
}

int changePosition(Player *player)
{
	if (theta >= 300.0)
		player->direction  = 0;
	else if (theta >= 240.0)
		player->direction  = 1;
	else if (theta >= 180.0)
		player->direction  = 2;
	else if (theta >= 120.0)
		player->direction  = 3;
	else if (theta >= 60.0)
		player->direction  = 4;
	else
		player->direction  = 5;

	switch (player->direction)
	{
		case 0:
			player->pos[0] -= player->speed;
			break;
		case 1:
			player->pos[0] += player->speed;
			break;
		case 2:
			player->pos[1] -= player->speed;
			break;
		case 3:
			player->pos[1] += player->speed;
			break;
		case 4:
			player->pos[2] -= player->speed;
			break;
		case 5:
			player->pos[2] += player->speed;
			break;
	}
}

//
//  void initScene()
//
//  A basic scene initialization function; should be called once after the
//      OpenGL context has been created. Doesn't need to be called further.
//      Just sets up a few function calls so that our main() is cleaner.
//
void initScene() {
    glEnable(GL_DEPTH_TEST);

    //this is some code to enable a default light for the scene;
    //feel free to play around with this, but we won't talk about
    //lighting in OpenGL for another couple of weeks yet.
    float lightCol[4] = { 1, 1, 1, 1};
    float ambientCol[4] = {0.0, 0.0, 0.0, 1.0};
    float lPosition[4] = { 10, 10, 10, 1 };
    glLightfv(GL_LIGHT0,GL_POSITION,lPosition);
    glLightfv(GL_LIGHT0,GL_DIFFUSE,lightCol);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientCol);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    //tell OpenGL not to use the material system; just use whatever we pass with glColor()
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    //tells OpenGL to blend colors across triangles. Once lighting is
    //enabled, this means that objects will appear smoother - if your object
    //is rounded or has a smooth surface, this is probably a good idea;
    //if your object has a blocky surface, you probably want to disable this.
    glShadeModel(GL_SMOOTH);
}

//
//  void updateDisplay()
//
//  GLUT callback for scene rendering. Sets up the modelview matrix, renders
//      a teapot to the back buffer, and switches the back buffer with the
//      front buffer (what the user sees).
//
void updateDisplay() {
    //update the modelview matrix based on the camera's position
    glMatrixMode(GL_MODELVIEW);             //make sure we aren't changing the projection matrix!
    glLoadIdentity();

	gluLookAt(cameraX, cameraY, cameraZ,     //camera is located at (x,y,z)
	            //myPlayer.xPos, myPlayer.yPos, myPlayer.zPos,            //camera is looking at (0,0,0)
	            0.0, 0.0, 0.0,
	            0.0f, 1.0f, 0.0f);           //up vector is (0,1,0) (positive Y)

	//clear the render buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if (checkInput(player1, room) == 1)
	{	
	    glColor3f(1.0,0.0,0.1);
	    changePosition(&player1);
	    drawPlayer(player1);
	}
	
	if (checkInput(player2, room) == 1)
	{
	    glColor3f(0.1,0.0,1.0);
	    changePosition(&player2);
	    drawPlayer(player2);
	}
	
	glColor3f(0.9,1.0,0.9);
	drawRoom(room);
	
	//push the back buffer to the screen
	glutSwapBuffers();
}

//
// void myTimer(int value)
//
//  We have to take an integer as input per GLUT's timer function prototype;
//  but we don't use that value so just ignore it. We'll register this function
//  once at the start of the program, and then every time it gets called it
//  will perpetually re-register itself and tell GLUT that the screen needs
//  be redrawn. yes, I said "needs be."
//
void myTimer(int value) {
	theta += 1.0;
	if (theta >= 360.0)
		theta -= 360.0;

	//std::cout << player1.pos[0] << ", " << player1.pos[1] << ", " << player1.pos[2] << std::endl;
		
	// can be called by tick() during later implementation
	glutPostRedisplay();
	glutTimerFunc((unsigned int)(1000.0 / 60.0), myTimer, 0);
}

//
//  int main(int argc, char **argv)
//
//      No offense but you should probably know what the main function does.
//      argc is the number of arguments to the program (program name included),
//      and argv is an array of strings; argv[i] is the ith command line
//      argument, where 0 <= i <= argc
//
int main(int argc, char **argv) {
    //create a double-buffered GLUT window at (50,50) with predefined windowsize
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(50,50);
    glutInitWindowSize(windowWidth,windowHeight);
    glutCreateWindow("glOutput Test");

    //give the camera a 'pretty' starting point!
    cameraRadius = 12.0f;
    cameraTheta = 2.80;
    cameraPhi = 2.0;
 	
 	player1.pos[0] = 0.5f; player1.pos[1] = 0.5f; player1.pos[2] = 0.5f; player1.length = 0.5f;
 	player1.direction = 0; player1.speed = 0.05f;
 	
 	player2.pos[0] = 1.0; player2.pos[1] = 1.0; player2.pos[2] = 1.0; player2.length = 0.4;
 	player2.direction = 0; player2.speed = 0.003f;
 	
 	room.pos[0] = 0.0; room.pos[1] = 0.0; room.pos[2] = 0.0; room.length = 10.0;
 	
    computeArcballPosition(cameraTheta, cameraPhi, cameraRadius,
						   cameraX, cameraY, cameraZ);
	
    //register callback functions...
    glutDisplayFunc(updateDisplay);
    glutReshapeFunc(resizeWindow);

    //do some basic OpenGL setup
    initScene();
    glutTimerFunc((unsigned int)(1000.0 / 60.0), myTimer, 0);

    //and enter the GLUT loop, never to exit.
    glutMainLoop();

    return(0);
}
